package utils

import (
	"context"
	"errors"
	"fmt"
	"github.com/zeromicro/go-zero/core/logx"
	"net/http"
	"sync"
)

const (
	// ApplicationJson stands for application/json.
	ApplicationJson = "application/json"
	// ContentType is the header key for Content-Type.
	ContentType = "Content-Type"
	// JsonContentType is the content type for JSON.
	JsonContentType = "application/json; charset=utf-8"
	TxtContentType  = "application/txt; charset=utf-8"
)
const (
	// ContentEncoding means Content-Encoding.
	ContentEncoding = "Content-Encoding"
	// ContentSecurity means X-Content-Security.
	ContentSecurity = "X-Content-Security"
	// KeyField means key.
	KeyField = "key"
	// SecretField means secret.
	SecretField = "secret"
	// TypeField means type.
	TypeField = "type"
	// CryptionType means cryption.
	CryptionType = 1
)

var (
	okLock    sync.RWMutex
	okHandler func(context.Context, any) any
)

// OkJsonCtx writes v into w with 200 OK.
func OkTxtCtx(ctx context.Context, w http.ResponseWriter, v any) {
	okLock.RLock()
	handlerCtx := okHandler
	okLock.RUnlock()
	if handlerCtx != nil {
		v = handlerCtx(ctx, v)
	}
	WriteTxtCtx(ctx, w, http.StatusOK, v)
}

// WriteTxtCtx writes v as txt string into w with code.
func WriteTxtCtx(ctx context.Context, w http.ResponseWriter, code int, v any) {
	if err := doWriteTxt(w, code, v); err != nil {
		logx.WithContext(ctx).Error(err)
	}
}

func doWriteTxt(w http.ResponseWriter, code int, v any) error {
	s, ok := v.(string)
	if !ok {
		http.Error(w, "", http.StatusInternalServerError)
		return fmt.Errorf("marshal txt failed, error: %w", nil)
	}

	w.Header().Set(ContentType, TxtContentType)
	w.WriteHeader(code)

	if n, err := w.Write([]byte(s)); err != nil {
		// http.ErrHandlerTimeout has been handled by http.TimeoutHandler,
		// so it's ignored here.
		if !errors.Is(err, http.ErrHandlerTimeout) {
			return fmt.Errorf("write response failed, error: %w", err)
		}
	} else if n < len(s) {
		return fmt.Errorf("actual bytes: %d, written bytes: %d", len([]byte(s)), n)
	}

	return nil
}
